#include "LecteurPhraseAvecArbre.h"
#include "Arbre.h"
#include <stdlib.h>
#include <iostream>
#include <string>
#include <fstream>
#include "Exception.h"
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich, string fichecrit) :
ls(nomFich), ts() {
    f.open(fichecrit.data(), ios_base::out | ios_base::trunc);
    if (f.fail()) {
        cout << " erreur d'ouverture" << endl;
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::ecrirefichier(string t) {
    f.write(t.data(), t.size());
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER

    sauterSymCour("debut");
    ecrirefichier(
            "#include <iostream> \n"
            "#include <stdlib.h> \n"
            "using namespace std; \n \n "
            "int main() {\n \n");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    ecrirefichier("return 1; \n}");
    testerSymCour("<FINDEFICHIER>");
    return si;
}

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }
    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        ecrirefichier("\n");
        sauterSymCour(";");
    } while (ls.getSymCour() == "<VARIABLE>" ||
            ls.getSymCour() == "si" ||
            ls.getSymCour() == "switch" ||
            ls.getSymCour() == "tantque" ||
            ls.getSymCour() == "repeter" ||
            ls.getSymCour() == "lire" ||
            ls.getSymCour() == "ecrire" ||
            ls.getSymCour() == "ecrire_ligne" ||
            ls.getSymCour() == "pour");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> |<instlire> |<instEcrire>

    if (ls.getSymCour() == "si") {
        return instSi();
    } else if (ls.getSymCour() == "switch") {
        return instSwitch();
    } else if (ls.getSymCour() == "tantque") {
        return instTq();
    } else if (ls.getSymCour() == "repeter") {
        return instRepeter();
    } else if (ls.getSymCour() == "lire") {
        return instLire();
    } else if (ls.getSymCour() == "ecrire") {
        return instEcrire();
    } else if (ls.getSymCour() == "ecrire_ligne") {
        return instEcrire_ligne();
    } else if (ls.getSymCour() == "pour") {
        return instPour();
    } else if (ls.getSymCour() == "<VARIABLE>") {
        return affectation();
    } else {
        errdebut e;
        throw e;
    }
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression> | <chaine>
    testerSymCour("<VARIABLE>");
    Noeud* var;
    if (!ts.chercherSymbole(ls.getSymCour()))
        ecrirefichier("int ");
    ecrirefichier(ls.getSymCour().getChaine());
    ecrirefichier("=");
    var = ts.chercheAjoute(ls.getSymCour());
    Noeud* exp;
    ls.suivant();
    sauterSymCour("=");
    if (ls.getSymCour() == "<CHAINE>") {
        ecrirefichier(ls.getSymCour().getChaine());
        exp = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else {
        exp = expression();
    }
    NoeudAffectation* aff = new NoeudAffectation(var, exp);
    ecrirefichier(";");
    return aff;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi() { //<instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> }[ sinon <seqInst> ] finsi
    sauterSymCour("si");
    sauterSymCour("(");
    ecrirefichier("if(");
    NoeudSi* si = new NoeudSi();
    Noeud* exp = expBool();
    ecrirefichier(") {\n");
    sauterSymCour(")");
    Noeud* sq = seqInst();
    ecrirefichier("} \n");
    si->ajouteSi(exp, sq);
    while (ls.getSymCour() == "sinonsi") {
        ecrirefichier("else if(");
        ls.suivant();
        sauterSymCour("(");
        Noeud* exp = expBool();
        ecrirefichier("){\n");
        sauterSymCour(")");
        Noeud* sq = seqInst();
        si->ajouteSi(exp, sq);
        ecrirefichier("}\n");
    }
    if (ls.getSymCour() == "sinon") {
        ecrirefichier("else{\n");
        ls.suivant();
        Noeud* sq = seqInst();
        si->ajouteSinon(sq);
        ecrirefichier("}\n");
    }
    sauterSymCour("finsi");
    return si;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSwitch() {//<instSwitch>    ::= switch ( <variable> ) case (<entier>) <seqinst>  { case (<entier>) <seqinst> }
    sauterSymCour("switch");
    sauterSymCour("(");
    ecrirefichier("switch(");
    testerSymCour("<VARIABLE>");
    if (!ts.chercherSymbole(ls.getSymCour()))
        ecrirefichier("int ");
    ecrirefichier(ls.getSymCour().getChaine());
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    NoeudSwitch* swi = new NoeudSwitch(var);
    sauterSymCour(")");
    ecrirefichier(") {\n");
    while (ls.getSymCour() == "case") {
        ls.suivant();
        sauterSymCour("(");
        ecrirefichier("case ");
        ecrirefichier(ls.getSymCour().getChaine());
        Noeud* inti = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
        sauterSymCour(")");
        ecrirefichier(": {\n");
        Noeud* sq = seqInst();
        ecrirefichier("break; } \n");
        swi->ajouteCase(inti, sq);
    }
    sauterSymCour("finswitch");
    ecrirefichier("} \n");
    return swi;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq() { //  <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque

    sauterSymCour("tantque");
    sauterSymCour("(");
    ecrirefichier("while(");
    Noeud* exp = expBool();
    sauterSymCour(")");
    sauterSymCour("faire");
    ecrirefichier("){\n");
    Noeud* si = seqInst();
    ecrirefichier("}\n");
    sauterSymCour("fintantque");
    NoeudInstTq * tq = new NoeudInstTq(exp, si);
    return tq;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire() { //      <instLire>      ::= lire ( <variable> )
    sauterSymCour("lire");
    sauterSymCour("(");

    testerSymCour("<VARIABLE>");
    if (!ts.chercherSymbole(ls.getSymCour())) {
        ecrirefichier("int ");
        ecrirefichier(ls.getSymCour().getChaine());
        ecrirefichier(";\n");
    }
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ecrirefichier("cin >> ");
    ecrirefichier(ls.getSymCour().getChaine());
    ls.suivant();
    sauterSymCour(")");
    NoeudInstLire * lir = new NoeudInstLire(var);
    ecrirefichier(";");
    return lir;

}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire() { //      <instEcrire>    ::= ecrire ( <expression> | <chaine> )
    sauterSymCour("ecrire");
    sauterSymCour("(");
    ecrirefichier("cout << ");
    Noeud* exp;
    exp = expression();
    NoeudInstEcrire * Ecr = new NoeudInstEcrire(exp);
    sauterSymCour(")");
    ecrirefichier(";");
    return Ecr;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire_ligne() { //      <instEcrire>    ::= ecrire ( <expression> | <chaine> )
    sauterSymCour("ecrire_ligne");
    sauterSymCour("(");
    Noeud* exp;
    ecrirefichier("cout << ");
    exp = expression();
    NoeudInstEcrire_ligne * Ecr = new NoeudInstEcrire_ligne(exp);
    sauterSymCour(")");
    ecrirefichier(" << endl");
    ecrirefichier(";");
    return Ecr;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter() { //  <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )

    sauterSymCour("repeter");
    ecrirefichier("do{\n");
    Noeud* si = seqInst();
    sauterSymCour("jusqua");
    ecrirefichier("}while(");
    sauterSymCour("(");
    Noeud* exp = expBool();
    sauterSymCour(")");
    ecrirefichier(");");
    NoeudInstRepeter* rep = new NoeudInstRepeter(exp, si);
    return rep;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour() { //      <instPour>      ::= pour <variable> dans <expression>..<expression> faire <Seqinst> finpour
    sauterSymCour("pour");
    ecrirefichier("for(");
    if (!ts.chercherSymbole(ls.getSymCour()))
        ecrirefichier("int ");
    testerSymCour("<VARIABLE>");
    string t = ls.getSymCour().getChaine();
    ecrirefichier(t);
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("dans");
    ecrirefichier("=");
    Noeud* exp1 = expression();
    ecrirefichier(";");
    ecrirefichier(t);
    ecrirefichier("<=");
    sauterSymCour("..");
    Noeud* exp2 = expression();
    sauterSymCour("faire");
    ecrirefichier(";");
    ecrirefichier(t);
    ecrirefichier("++){\n");
    Noeud* si = seqInst();
    sauterSymCour("finpour");
    ecrirefichier("}\n");
    NoeudInstPour* pour = new NoeudInstPour(var, exp1, exp2, si);
    return pour;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool() { //      <expArith> { <opOu> <expArith> }

    Noeud* arith = expConj();
    while (ls.getSymCour() == "ou") {
        ecrirefichier(" || ");
        Symbole operateur = opOu();
        Noeud* arithdroit = expConj();
        arith = new NoeudOperateurOu(operateur, arith, arithdroit); // const. du noeud
    }
    return arith;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expConj() { //      <relation> { <opEt> <relation> }

    Noeud* rel = relation();
    while (ls.getSymCour() == "et") {
        ecrirefichier(" && ");
        Symbole operateur = opEt();
        Noeud* reldroit = relation();
        rel = new NoeudOperateurEt(operateur, rel, reldroit); // const. du noeud
    }
    return rel;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() { // <expression> { <opBool> <expression> }

    Noeud* exp = expression();
    while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<=" ||
            ls.getSymCour() == ">=" || ls.getSymCour() == ">" || ls.getSymCour() == "<") {
        Symbole operateur = opBool();
        ecrirefichier(operateur.getChaine());
        Noeud* expdroit = expression();
        exp = new NoeudOperateurBool(operateur, exp, expdroit); // const. du noeud
    }
    return exp;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <terme> { <opAdd> <terme> }

    Noeud* t = terme();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        Symbole operateur = opAdd();
        ecrirefichier(operateur.getChaine());
        Noeud* tdroit = terme();
        t = new NoeudOperateurAdd(operateur, t, tdroit); // const. du noeud
    }
    return t;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() { //<facteur> { <opMult> facteur> }

    Noeud* fact = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        try {
            Symbole operateur = opMult();
            ecrirefichier(operateur.getChaine());
            Noeud* factdroit = facteur();
            fact = new NoeudOperateurMult(operateur, fact, factdroit); // const. du noeud
        } catch (div0 e) {
            cout << e.what() << endl;
        }
    }
    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud * LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <entier>  |  <variable>  | <opUnaire>  <expBool> | ( <expBool> )

    Noeud * fact = NULL;
    if ((ls.getSymCour() == "-") || (ls.getSymCour() == "non")) {
        ecrirefichier("!");
        Symbole operateur = opUnaire();
        fact = new NoeudOperateurUnaire(operateur, 0, expBool());
    } else if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>") {
        ecrirefichier(ls.getSymCour().getChaine());
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "(") { // ( <expbool> )
        ecrirefichier("(");
        ls.suivant();
        fact = expBool();
        ecrirefichier(")");
        sauterSymCour(")");
    } else
        erreur("<facteur>");

    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opEt() { // et 
    Symbole operateur;
    if (ls.getSymCour() == "et") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opEt>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opOu() { // ou 
    Symbole operateur;
    if (ls.getSymCour() == "ou") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opOu>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opBool() { // == | != | <= | >= | < | >
    Symbole operateur;
    if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<=" || ls.getSymCour() == ">="
            || ls.getSymCour() == ">" || ls.getSymCour() == "<") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opBool>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd() { // + | -
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() { // * | /
    Symbole operateur;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire()// - | non
{
    Symbole operateur;
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opUnaire>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterCommentaire() {
    while (ls.iscommentaire()) {
        ecrirefichier("//");
        ecrirefichier(ls.getSymCour().getChaine());
        ecrirefichier("\n");
        ls.suivant();
    }
}
////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    sauterCommentaire();
    if (ls.getSymCour() != ch) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne()
                << " - Colonne " << ls.getColonne() << endl << "   Attendu : "
                << ch << endl << "   Trouve  : " << ls.getSymCour() << endl
                << endl;
        Exerreur e;
        throw e;
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);

    ls.suivant();
    sauterCommentaire();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    Exerreur e;
    throw e;
}
